from core.DataStruct import WhiteBase, BlackBase, BlackMixColorant, PureColorant, StepColorant, Colorant
import xlrd
import numpy as np
import matplotlib.pyplot as plt
import math

class DataHandler:
    def __init__(self, group, config):
        self.group = group
        self.whiteBase = WhiteBase()
        self.blackBase = BlackBase()
        self.stepsIndex = []
        self.blackPure = PureColorant()
        self.sheet = None
        self.sheet2 = None
        self.config = config
        self.Rr = 0
        self.pureRr = 0
        self.blackRr = 3.0            # 纯黑除掉；
        self.blackMix = []          # 黑混；
        self.black = StepColorant()  # 黑混过度样本；
        self.r = []                         # 线性相关性；

    def getDataFromExcel(self, excelPath):
        """
        从excel中获取所有的数据； 并计算K/Sw, S/Sw, SD(白）
        :param exelPath:            梯度样本文件；
        :return:
        """
        data = xlrd.open_workbook(excelPath)
        self.sheet = data.sheet_by_index(0)

        self.__getPureWhite()                       # 获取基准白；
        if self.config.blackRefFilePath:
            self.__getPureBlack(self.config.blackRefFilePath)        # 获取指定的纯黑；        纯黑指定；

        self.__getWhiteBlackBase()                  # 黑白基底；
        self.__getStepColorants()                   # 获取梯度；
        self.__getPureColorant()                    # 获取纯色；

        if self.config.calibrationFilePath:
            self.__getBlackMix(self.config.calibrationFilePath)             # 获取黑混;
            self.__getBlack()                           # 黑混过度；

        self.group.K1 = self.__calcK1()
        self.__calcActual()                         # 计算无限厚情况下的真实反射率；   黑白底半透明理论计算；
        # 下面的计算最为关键；  这里的精度直接决定了配色的精度；
        self.group.calcPureWhiteKSw()               # 基准白 K/SW, S/SW计算；
        self.group.calcSD()                         # 白色的厚度参数；
        self.__calcKSBySw()                         # 计算K/Sw, S/Sw  梯度样；
        self.__calcStepSD()                         # 计算每一个梯度的厚度参数， 用来做质量，体积之间的转换；
        #self.__calcFitAB()                          # 计算拟合参数；

        # 单色检测； 对每一个梯度进行计算；
        #self.__checkSingleStep()

    def __calcStepSD(self):
        """
        计算梯度的SD；
        :param base:
        :param pureWhite:
        :return:
        """
        for i in range(len(self.group.colorants)):
            # workbook = xlwt.Workbook(encoding='utf-8')
            # worksheet = workbook.add_sheet('My Worksheet')
            nStep = len(self.group.colorants[i].step)
            for j in range(nStep):
                self.group.colorants[i].step[j].calcSD(self.group.baseWhite, self.group.pureWhite)
            for k in range(32):
                B = np.zeros((nStep, 1))
                A = np.zeros((nStep, 4))
                for j in range(nStep):
                    # worksheet.write(j, 0, self.group.colorants[i].step[j].compose.quality[0])
                    B[j][0] = self.group.colorants[i].step[j].weightToV[k]
                    tmpCC = self.group.colorants[i].step[j].compose.quality[0] / sum(self.group.colorants[i].step[j].compose.quality)
                    A[j][0] = math.pow(tmpCC, 3)
                    A[j][1] = math.pow(tmpCC, 2)
                    A[j][2] = math.pow(tmpCC, 1)
                    A[j][3] = 1
                ATA = np.dot(A.T, A)
                ATB = np.dot(A.T, B)
                res = np.dot(np.linalg.inv(ATA), ATB)
                self.group.colorants[i].param.append(res)
            # workbook.save(self.group.colorants[i].colorantType + ".xlsx")

    def __getPureWhite(self):
        tmpName1 = self.sheet.cell_value(1, 0)
        tmpName2 = self.sheet.cell_value(2, 0)
        print(tmpName1 + ", " + tmpName2)
        tmpPure = PureColorant()
        tmpPure.colorantType = "白"
        tmpString = tmpName1.split(" ")
        if tmpString[1] == "白底":
            for j in range(31):
                tmpPure.whiteRef[j] = self.sheet.cell_value(1, self.config.ref400Offset + j) * 0.01
        elif tmpString[1] == "黑底":
            for j in range(31):
                tmpPure.blackRef[j] = self.sheet.cell_value(1, self.config.ref400Offset + j) * 0.01
        tmpString2 = tmpName2.split(" ")
        if tmpString2[1] == "白底":
            for j in range(31):
                tmpPure.whiteRef[j] = self.sheet.cell_value(2, self.config.ref400Offset + j) * 0.01
        elif tmpString2[1] == "黑底":
            for j in range(31):
                tmpPure.blackRef[j] = self.sheet.cell_value(2, self.config.ref400Offset + j) * 0.01
        tmpPure.calc710()
        self.group.pureWhite = tmpPure

    def __calcFitAB(self):
        for i in range(len(self.group.colorants)):
            self.group.colorants[i].fitSBySw()
            # self.group.colorants[i].testFit()

    def __calcR(self):
        """
        计算线性相关性；
        :return:
        """
        for i in range(len(self.group.colorants)):
            nStep = len(self.group.colorants[i].step)
            for k in range(32):
                x = []
                y = []
                plt.figure(k+1, figsize=(10, 8))
                for j in range(nStep-1, 0, -1):
                    C1 = self.group.colorants[i].step[j].getC1()
                    C0 = self.group.colorants[i].step[j].getC0()
                    x.append(C1/(C0 + C1) * 100)
                    y.append(self.group.colorants[i].step[j].K_by_S[k])
                    if len(x) >= 3:
                        print(self.__calcCorr(x, y))
                plt.plot(x, y, linestyle='--', alpha=0.5, color='r', label='legend2')

    def __calcCorr(self, a, b):
        a_avg = sum(a) / len(a)
        b_avg = sum(b) / len(b)
        # 计算分子，协方差————按照协方差公式，本来要除以n的，由于在相关系数中上下同时约去了n，于是可以不除以n
        cov_ab = sum([(x - a_avg) * (y - b_avg) for x, y in zip(a, b)])
        # 计算分母，方差乘积————方差本来也要除以n，在相关系数中上下同时约去了n，于是可以不除以n
        sq = math.sqrt(sum([(x - a_avg) ** 2 for x in a]) * sum([(x - b_avg) ** 2 for x in b]))
        corr_factor = cov_ab / sq
        return corr_factor

    # 以下都是处理函数；
    def __calcKSBySw(self):
        # 计算k/sw, s/sw
        if self.group.calibrateBlack:
            self.group.calibrateBlack.calcKSBySw(self.group.pureWhite, self.group.pureBlack)            # 标定黑， 计算K/Sw, S/Sw
        for i in range(len(self.group.colorants)):
            for j in range(len(self.group.colorants[i].step)):          # 梯度；
                self.group.colorants[i].step[j].calcKSBySw(self.group.pureWhite, self.group.colorants[i].pure)
                if self.group.colorants[i].blackMix:
                    self.group.colorants[i].blackMix.calcKSBySw(self.group.pureBlack, self.group.colorants[i].pure)
                    for k in range(32):
                        self.group.colorants[i].blackMix.S_by_Sw[k] *= self.group.calibrateBlack.S_by_Sw[k]
                        self.group.colorants[i].blackMix.K_by_Sw[k] *= self.group.calibrateBlack.K_by_Sw[k]
                    self.group.colorants[i].dealBlackMix()
        # self.group.save()

    def __getBlack(self):
        """
        黑与白的混合， 用于算K/S， 在指定浓度下的；               实际上这个部分可以通过拟合获取得到；
        :return:
        """
        self.group.calibrateBlack = StepColorant()
        for i in range(31):
            self.group.calibrateBlack.actualRef[i] = self.sheet2.cell_value(6, self.config.ref400Offset + i) * 0.01
        self.group.calibrateBlack.actualRef[31] = 2 * self.group.calibrateBlack.actualRef[30] - self.group.calibrateBlack.actualRef[29]
        self.group.calibrateBlack.compose.quality.append(0.08)
        self.group.calibrateBlack.compose.quality.append(50)
        self.group.calibrateBlack.compose.comp.append("黑")
        self.group.calibrateBlack.compose.comp.append("白")
        self.group.calibrateBlack.bWhiteAndBlack = False                       # 不存在黑白底数据， 就一个数据；

    def __getBlackMix(self, excelPath):
        data = xlrd.open_workbook(excelPath)
        self.sheet2 = data.sheet_by_index(0)
        for i in range(3):
            tmpBlackMix = BlackMixColorant()
            tmpName1 = self.sheet2.cell_value(2 * i, 0)
            tmpName2 = self.sheet2.cell_value(2 * i + 1, 0)
            tmpString1 = tmpName1.split(" ")
            tmpString2 = tmpName2.split(" ")
            if tmpString1[len(tmpString1) - 2] == "白底":
                for j in range(31):
                    tmpBlackMix.whiteRef[j] = self.sheet2.cell_value(2 * i, self.config.ref400Offset + j) * 0.01
            elif tmpString1[len(tmpString1) - 2] == "黑底":
                for j in range(31):
                    tmpBlackMix.blackRef[j] = self.sheet2.cell_value(2 * i, self.config.ref400Offset + j) * 0.01
            if tmpString2[len(tmpString2) - 2] == "白底":
                for j in range(31):
                    tmpBlackMix.whiteRef[j] = self.sheet2.cell_value(2 * i + 1, self.config.ref400Offset + j) * 0.01
            elif tmpString2[len(tmpString2) - 2] == "黑底":
                for j in range(31):
                    tmpBlackMix.blackRef[j] = self.sheet2.cell_value(2 * i + 1, self.config.ref400Offset + j) * 0.01
            tmpQuality1 = float(tmpString1[3])
            tmpQuality2 = float(tmpString1[5])
            tmpBlackMix.compose.quality.append(tmpQuality1)
            tmpBlackMix.compose.quality.append(tmpQuality2)
            tmpBlackMix.compose.comp.append(tmpString1[2])
            tmpBlackMix.compose.comp.append(tmpString1[4])
            tmpColorantType = tmpString1[2]
            tmpBlackMix.colorantType = tmpColorantType
            tmpBlackMix.calc710()
            for k in range(len(self.group.colorants)):
                if self.group.colorants[k].colorantType == tmpColorantType:
                    self.group.colorants[k].blackMix = tmpBlackMix

    def __calcKByS(self):
        """
        计算纯样， 梯度样本， 黑混样本的K/S;
        :return:
        """
        # workbook = xlwt.Workbook(encoding='utf-8')
        # worksheet = workbook.add_sheet('My Worksheet')
        # row = 0
        # self.pureRr = 0.01                                                                # 测试用；
        self.group.pureBlack.calcKByS(self.group.K1)
        self.group.pureWhite.calcKByS(self.group.K1)
        self.group.calibrateBlack.calcKByS(self.group.K1)
        for i in range(len(self.group.colorants)):
            self.group.colorants[i].pure.calcKByS(self.pureRr)
            if self.group.colorants[i].blackMix:
                self.group.colorants[i].blackMix.calcKByS(self.group.K1)
            for j in range(len(self.group.colorants[i].step)):
                self.group.colorants[i].step[j].calcKByS(self.group.K1)
        self.group.saveKbyS()

    def __calcActual(self):
        """
        计算真实反射率；
        :return:
        """
        #test
        if self.group.pureBlack:
            self.group.pureBlack.calcKByS(self.pureRr)
        if self.group.calibrateBlack:
            self.group.calibrateBlack.RemoveK1K2AndCalcActualRef(self.group.K1, self.whiteBase, self.blackBase)                                     # 黑标定；
        if self.group.pureWhite:
            self.group.pureWhite.RemoveK1K2AndCalcActualRef(self.group.K1, self.whiteBase, self.blackBase)                                          # 纯白；
        for i in range(len(self.group.colorants)):
            self.group.colorants[i].pure.RemoveK1K2AndCalcActualRef(self.pureRr, self.whiteBase, self.blackBase)                              # 纯样；
            if self.group.colorants[i].blackMix:                                                # 黑混；
                self.group.colorants[i].blackMix.RemoveK1K2AndCalcActualRef(self.group.K1, self.whiteBase, self.blackBase)
            for j in range(len(self.group.colorants[i].step)):
                self.group.colorants[i].step[j].RemoveK1K2AndCalcActualRef(self.group.K1, self.whiteBase, self.blackBase)

    def __getWhiteBlackBase(self):
        """
        黑白基底；
        :return:
        """
        self.whiteBase.name = self.sheet.cell_value(1, 0)
        self.blackBase.name = self.sheet.cell_value(2, 0)
        for i in range(31):
            self.whiteBase.ref[i] = float(self.sheet.cell_value(self.config.whiteBlackBaseRow, 3 + i)) * 0.01
            self.blackBase.ref[i] = float(self.sheet.cell_value(self.config.whiteBlackBaseRow + 1, 3 + i)) * 0.01
        self.whiteBase.cal710()
        self.blackBase.cal710()
        self.group.baseWhite = self.whiteBase
        self.group.baseBlack = self.blackBase

    def __getPureColorant(self):
        """
        获取纯色；
        :return:
        """
        pureIndex = []
        ratio = 1.0
        for i in range(self.config.nColorant):      # 除黑； 黑单独载入；
            tmpName1 = self.sheet.cell_value(self.config.pureRow + 2 * i, 0)
            tmpName2 = self.sheet.cell_value(self.config.pureRow + 2 * i + 1, 0)
            print(tmpName1 + ", " + tmpName2)
            tmpPure = PureColorant()
            tmpString = tmpName1.split(" ")
            if tmpString[1] == "白底":
                for j in range(31):
                    tmpPure.whiteRef[j] = self.sheet.cell_value(2 * i + self.config.pureRow, self.config.ref400Offset + j) * 0.01 * ratio
            elif tmpString[1] == "黑底":
                for j in range(31):
                    tmpPure.blackRef[j] = self.sheet.cell_value(2 * i + self.config.pureRow, self.config.ref400Offset + j) * 0.01 * ratio
            tmpString2 = tmpName2.split(" ")
            if tmpString2[1] == "白底":
                for j in range(31):
                    tmpPure.whiteRef[j] = self.sheet.cell_value(2 * i + self.config.pureRow + 1, self.config.ref400Offset + j) * 0.01 * ratio
            elif tmpString2[1] == "黑底":
                for j in range(31):
                    tmpPure.blackRef[j] = self.sheet.cell_value(2 * i + self.config.pureRow + 1, self.config.ref400Offset + j) * 0.01 * ratio
            tmpPure.calc710()
            for j in range(len(self.group.colorants)):
                if self.group.colorants[j].colorantType == tmpString[0]:
                    tmpPure.colorantType = tmpString[0]
                    self.group.colorants[j].pure = tmpPure
                    break

    def __getStepColorants(self):
        """
        获取梯度多基底样本；
        :return:
        """
        curColorantType = ""
        curColorantObj = None
        tmpIndex = []
        for i in range(self.config.sampleNum):
            tmpName1 = self.sheet.cell_value(2 * i + self.config.sampleStartRow, 0)
            tmpName2 = self.sheet.cell_value(2 * i + self.config.sampleStartRow + 1, 0)
            tmpString1 = tmpName1.split(" ")
            tmpString2 = tmpName2.split(" ")
            tmpStepColorant = StepColorant()

            if curColorantType != tmpString1[2]:        # 色料；
                if curColorantObj:
                    self.group.colorants.append(curColorantObj)
                curColorantObj = Colorant()
                curColorantObj.pureWhite = self.group.pureWhite
                curColorantType = tmpString1[2]
                curColorantObj.colorantType = curColorantType

            if tmpString1[len(tmpString1) - 1] == "白底":
                for j in range(31):
                    tmpStepColorant.whiteRef[j] = self.sheet.cell_value(2 * i + self.config.sampleStartRow, self.config.ref400Offset + j) * 0.01
            elif tmpString1[len(tmpString1) - 1] == "黑底":
                for j in range(31):
                    tmpStepColorant.blackRef[j] = self.sheet.cell_value(2 * i + self.config.sampleStartRow, self.config.ref400Offset + j) * 0.01
            if tmpString2[len(tmpString2) - 1] == "白底":
                for j in range(31):
                    tmpStepColorant.whiteRef[j] = self.sheet.cell_value(2 * i + self.config.sampleStartRow + 1, self.config.ref400Offset + j) * 0.01
            elif tmpString2[len(tmpString2) - 1] == "黑底":
                for j in range(31):
                    tmpStepColorant.blackRef[j] = self.sheet.cell_value(2 * i + self.config.sampleStartRow + 1, self.config.ref400Offset + j) * 0.01
            tmpQuality1 = float(tmpString1[1])          # 白；
            tmpQuality2 = float(tmpString1[3])          # 色料；
            tmpColorantType = tmpString1[2]

            # 成分；
            tmpStepColorant.compose.comp.append(tmpColorantType)
            tmpStepColorant.compose.comp.append(tmpString1[0])
            tmpStepColorant.compose.quality.append(tmpQuality2)
            tmpStepColorant.compose.quality.append(tmpQuality1)
            tmpStepColorant.calc710()
            curColorantObj.step.append(tmpStepColorant)
        self.group.colorants.append(curColorantObj)

    def __getPureBlack(self, excelPath):
        """
        获取纯黑；
        :param excelPath:
        :return:
        """
        self.group.pureBlack = PureColorant()
        blackData = xlrd.open_workbook(excelPath)
        blackSheet = blackData.sheet_by_index(0)
        for i in range(31):
            self.group.pureBlack.actualRef[i] = (blackSheet.cell_value(1, 3 + i) - self.blackRr) * 0.01
        self.group.pureBlack.actualRef[31] = 2 * self.group.pureBlack.actualRef[30] - self.group.pureBlack.actualRef[29]

    def __calcK1(self):
        """
        计算K1;
        :return:
        """
        minRef = 10000
        pureMinRef = 10000
        for i in range(len(self.group.colorants)):
            for j in range(32):
                if self.group.colorants[i].pure.whiteRef[j] < pureMinRef:
                    pureMinRef = self.group.colorants[i].pure.whiteRef[j]
                if self.group.colorants[i].pure.blackRef[j] < pureMinRef:
                    pureMinRef = self.group.colorants[i].pure.blackRef[j]
            for k in range(len(self.group.colorants[i].step)):
                for j in range(32):
                    if self.group.colorants[i].step[k].whiteRef[j] < minRef:
                        minRef = self.group.colorants[i].step[k].whiteRef[j]
                    if self.group.colorants[i].step[k].blackRef[j] < minRef:
                        minRef = self.group.colorants[i].step[k].blackRef[j]
        minRef = minRef - 0.001
        if minRef < 0.001:
            minRef = 0.001
        elif minRef > 0.04:
            minRef = 0.04
        self.group.K1 = minRef

        pureMinRef = pureMinRef - 0.001
        if pureMinRef <= 0.001:
            pureMinRef = 0.001
        elif pureMinRef > 0.04:
            pureMinRef = 0.04
        self.pureRr = pureMinRef
        return minRef

    ################################################################################################################
    # 下面的是拟合计算纯色， 现在没用；
    ################################################################################################################
    def __critionFunc(self, x, colorant, k):
        """
        判决函数；
        :param x            纯样值；
        :param colorant:    色料；
        :param k:           波长；
        :return:
        """
        tmpSum = 0
        for i in range(len(colorant.step)):
            C0 = colorant.step[i].getC0()
            C1 = colorant.step[i].getC1()
            tmpSSw  = C0/C1 * (colorant.step[i].K_by_S[k] - self.group.pureWhite.K_by_S[k])/(x - colorant.step[i].K_by_S[k])
            tmpKSw = x * tmpSSw
            tmpKByS = (C0 * self.group.pureWhite.K_by_S[k] + C1 * tmpKSw) / (C0 + C1 * tmpSSw)   # 混合；
            print(tmpKByS)

    def __calcPureKBySBySpider(self):
        """
        通过粒子群算法寻优得到K/S的优解
        :return:
        """
        for i in range(len(self.group.colorants)):
            for k in range(32):
                self.__critionFunc(10, self.group.colorants[i], k)

    def __calcPureByFit(self, pureWhite):
        """
        拟合计算纯样点；
        :param pureWhite:
        :return:
        """
        for i in range(len(self.group.colorants)):
            nStep = len(self.group.colorants[i].step)
            for k in range(32):
                idx = []
                x = []
                y = []
                for j in range(nStep - 1, 0, -1):
                    C1 = self.group.colorants[i].step[j].getC1()
                    C0 = self.group.colorants[i].step[j].getC0()
                    tmpKByS = self.group.colorants[i].step[j].K_by_S[k]
                    if len(y) > 0:
                        if tmpKByS < y[len(y) - 1]:
                            continue
                    idx.append(j)
                    x.append(C1 / (C0 + C1) * 100)
                    y.append(tmpKByS)
                for j in range(len(y)-3):
                    tmpCorr = self.__calcCorr(x, y)
                    if tmpCorr < 0.95:
                        x.pop(len(x) - 1)
                        y.pop(len(y) - 1)
                        idx.pop(len(idx) - 1)
                    else:
                        break
                A = np.zeros((len(idx), 2))
                B = np.zeros((len(idx), 1))
                for j in range(len(idx)):
                    C1 = self.group.colorants[i].step[idx[j]].getC1()
                    C0 = self.group.colorants[i].step[idx[j]].getC0()
                    A[j, 0] = C1
                    A[j, 1] = - C1 * self.group.colorants[i].step[idx[j]].K_by_S[k]
                    B[j, 0] = C0 * self.group.colorants[i].step[idx[j]].K_by_S[k] - C0 * pureWhite.K_by_S[k]
                ATA = np.dot(A.T, A)
                ATB = np.dot(A.T, B)
                tmpRes = np.dot(np.linalg.inv(ATA), ATB)
                print(tmpRes[0] / tmpRes[1])
                self.group.colorants[i].pure.K_by_S[k] = float(tmpRes[0]/tmpRes[1])

    def getColorantsForCombine(self):
        """
        获取指定的     白， 红， 绿， 蓝；
        :return:
        """
        colorants = self.group.colorants[1:4]
        for i in range(len(colorants)):
            for j in range(32):
                for k in range(3):
                    colorants[i].K_by_Sw[j] += colorants[i].step[len(colorants[i].step) - k - 1].K_by_Sw[j]
                    colorants[i].S_by_Sw[j] += colorants[i].step[len(colorants[i].step) - k - 1].S_by_Sw[j]
                colorants[i].K_by_Sw[j] /= 3
                colorants[i].S_by_Sw[j] /= 3
        return colorants
